/**
 * 语音 SSE 流播放器
 * 专门用于处理通过 Server-Sent Events 接收的语音流数据
 */

export interface VoiceSSEPlayerOptions {
  sampleRate?: number // 音频采样率，默认 22050
  timeout?: number // 连接超时时间，默认 30 秒
  retryOnError?: boolean // 是否在错误时重试，默认 false
  maxRetries?: number // 最大重试次数，默认 3
  onStart?: () => void // 开始播放回调
  onEnd?: () => void // 播放结束回调
  onError?: (error: string) => void // 错误回调
  onProgress?: (data: { duration: number; currentTime: number }) => void // 播放进度回调
}

export class VoiceSSEPlayer {
  private audioContext: AudioContext | null = null
  private eventSource: EventSource | null = null
  private nextStartTime = 0
  private sampleRate: number
  private timeout: number
  private callbacks: VoiceSSEPlayerOptions
  private isPlaying = false
  private startTime = 0
  private timeoutId: number | null = null
  private hasReceivedData = false
  private lastDataTime = 0
  private paddingDataCount = 0
  private maxPaddingDataCount = 3 // 连续收到3次填充数据就认为流结束
  private audioBuffer: ArrayBuffer[] = [] // 音频数据缓冲区
  private isBuffering = false // 是否正在缓冲
  private bufferPlaybackTimer: number | null = null // 缓冲播放定时器
  private maxBufferSize = 100 // 增加缓冲区大小，防止数据丢失
  private isStreamEnding = false // 流是否正在结束

  constructor(options: VoiceSSEPlayerOptions = {}) {
    this.sampleRate = options.sampleRate ?? 22050
    this.timeout = options.timeout ?? 30000 // 增加到30秒
    this.callbacks = options
  }

  /**
   * 开始播放语音 SSE 流
   * @param url SSE 流的 URL
   */
  async play(url: string): Promise<void> {
    // 如果正在播放，先停止当前播放
    if (this.isPlaying) {
      this.stop()
      // 等待停止完成
      await new Promise(resolve => setTimeout(resolve, 100))
    }

    try {
      this.isPlaying = true
      this.startTime = Date.now()
      this.hasReceivedData = false
      this.lastDataTime = Date.now()
      this.callbacks.onStart?.()

      // 初始化音频上下文
      this.initAudioContext()

      // 创建 EventSource 连接
      this.eventSource = new EventSource(url)

      // 监听音频数据事件
      this.eventSource.addEventListener('audio', this.handleAudioEvent.bind(this))

      // 监听错误信息事件
      this.eventSource.addEventListener('error', this.handleErrorEvent.bind(this))

      // 监听流结束事件
      this.eventSource.addEventListener('end', this.handleStreamEnd.bind(this))

      // 监听连接错误
      this.eventSource.onerror = this.handleConnectionError.bind(this)

      // 设置超时
      this.setupTimeout()

    } catch (error) {
      this.handleError(`播放失败: ${error}`)
    }
  }

  /**
   * 停止播放并清理资源
   */
  stop(): void {
    this.isPlaying = false
    
    // 清除超时定时器
    if (this.timeoutId) {
      clearTimeout(this.timeoutId)
      this.timeoutId = null
    }
    
    // 清除缓冲播放定时器
    if (this.bufferPlaybackTimer) {
      clearTimeout(this.bufferPlaybackTimer)
      this.bufferPlaybackTimer = null
    }
    
    // 停止缓冲播放
    this.isBuffering = false
    
    // 关闭 EventSource 连接
    if (this.eventSource) {
      this.eventSource.close()
      this.eventSource = null
    }

    // 重置音频上下文
    this.resetAudioContext()
    
    // 清理缓冲区
    this.audioBuffer = []
    
    // 重置状态
    this.isStreamEnding = false
    this.paddingDataCount = 0
    
    this.callbacks.onEnd?.()
  }

  /**
   * 获取当前播放状态
   */
  getStatus(): 'idle' | 'playing' {
    return this.isPlaying ? 'playing' : 'idle'
  }

  /**
   * 初始化音频上下文
   */
  private initAudioContext(): void {
    if (!this.audioContext) {
      this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
      
    }
    
    // 如果音频上下文被暂停，恢复它
    if (this.audioContext.state === 'suspended') {
      this.audioContext.resume()
    }
  }

  /**
   * 重置音频上下文
   */
  private resetAudioContext(): void {
    if (this.audioContext) {
      try {
        this.audioContext.close()
      } catch (e) {
        // 忽略关闭错误
      }
      this.audioContext = null
    }
    this.nextStartTime = 0
  }

  /**
   * 处理音频数据事件
   */
  private handleAudioEvent(event: MessageEvent): void {
    if (!this.isPlaying || !this.audioContext) return

    try {
      let base64Data = event.data

      // 检查是否是填充数据（全为 'A' 的重复数据）
      if (this.isPaddingData(base64Data)) {
        this.paddingDataCount++
        
        // 如果连续收到填充数据，说明流要关闭了
        if (this.paddingDataCount >= this.maxPaddingDataCount) {
          
          this.isStreamEnding = true
          // 立即关闭EventSource连接，不再接收新数据
          if (this.eventSource) {
            this.eventSource.close()
            this.eventSource = null
          }
          // 等待缓冲区播放完成
          this.waitForBufferCompletion()
        }
        return
      }
      
      // 如果流正在结束，忽略新的音频数据
      if (this.isStreamEnding) {
        
        return
      }
      
      // 重置填充数据计数
      this.paddingDataCount = 0

      // 清理和验证 base64 数据
      base64Data = this.cleanBase64Data(base64Data)

      // 解码为二进制数据
      const audioData = this.decodeBase64ToArrayBuffer(base64Data)

      // 检查音频数据是否有效（不是全零数据）
      if (this.isValidAudioData(audioData)) {
        // 检查缓冲区大小
        if (this.audioBuffer.length >= this.maxBufferSize) {
          this.audioBuffer.shift() // 移除最旧的数据
        }
        
        // 将音频数据添加到缓冲区
        this.audioBuffer.push(audioData)
        
        // 如果不在缓冲播放中，开始播放缓冲区
        if (!this.isBuffering) {
          this.startBufferPlayback()
        }
      }

      // 标记已收到数据并更新时间
      this.hasReceivedData = true
      this.lastDataTime = Date.now()

    } catch (error) {
      this.handleError(`音频数据处理失败: ${error}`)
    }
  }

  /**
   * 处理错误信息事件
   */
  private handleErrorEvent(event: MessageEvent): void {
    const errorMessage = event.data || '未知错误'
    
    // 如果错误信息为空或undefined，可能是正常的流结束，不当作错误处理
    if (!event.data || event.data === 'undefined' || event.data.trim() === '') {
      return
    }
    
    this.handleError(`后端返回错误: ${errorMessage}`)
  }

  /**
   * 处理连接错误
   */
  private handleConnectionError(): void {
    if (this.isPlaying) {
      // 如果连接已经关闭，可能是正常的流结束
      if (this.eventSource?.readyState === EventSource.CLOSED) {
        // 等待一段时间确保音频播放完成，然后正常结束
        setTimeout(() => {
          if (this.isPlaying) {
            this.stop()
          }
        }, 1000)
        return
      }
      
      // 如果连接状态是 OPEN (1)，说明连接正常，不应该触发错误
      if (this.eventSource?.readyState === EventSource.OPEN) {
        // 连接正常，可能是其他错误，直接报错
        this.handleError('SSE 连接错误')
        return
      }
      
      // 如果连接状态是 CONNECTING (0)，可能是连接问题
      if (this.eventSource?.readyState === EventSource.CONNECTING) {
        // 等待一段时间，如果还是连接中，说明连接失败
        setTimeout(() => {
          if (this.isPlaying && this.eventSource?.readyState === EventSource.CONNECTING) {
            this.handleError('SSE 连接超时')
          }
        }, 5000) // 等待5秒
        return
      }
      
      // 对于招呼语播放，不启用重试机制，避免重复播放
      this.handleError('SSE 连接错误')
    }
  }

  /**
   * 开始缓冲播放
   */
  private startBufferPlayback(): void {
    if (this.isBuffering || this.audioBuffer.length === 0) return
    
    this.isBuffering = true
    this.playNextBufferChunk()
  }

  /**
   * 等待缓冲区播放完成
   */
  private waitForBufferCompletion(): void {
    let checkCount = 0
    const maxChecks = 20 // 最多检查20次，避免无限循环
    let lastBufferSize = this.audioBuffer.length
    let stableCount = 0 // 缓冲区大小稳定的次数
    
    const checkBuffer = () => {
      if (!this.isPlaying) return
      
      checkCount++
      const currentBufferSize = this.audioBuffer.length
      
      // 检查缓冲区大小是否稳定
      if (currentBufferSize === lastBufferSize) {
        stableCount++
      } else {
        stableCount = 0
        lastBufferSize = currentBufferSize
      }
      
      // 如果缓冲区为空且不在缓冲，或者缓冲区大小稳定且流已结束
      if ((currentBufferSize === 0 && !this.isBuffering) || (this.isStreamEnding && stableCount >= 3)) {
        this.stop()
      } else if (checkCount >= maxChecks) {
        this.stop()
      } else {
        // 继续检查
        setTimeout(checkBuffer, 1000)
      }
    }
    
    // 开始检查
    setTimeout(checkBuffer, 1000)
  }

  /**
   * 播放下一个缓冲块
   */
  private playNextBufferChunk(): void {
    if (!this.isBuffering || this.audioBuffer.length === 0) {
      this.isBuffering = false
      return
    }
    
    const audioData = this.audioBuffer.shift()!
    
    // 播放音频数据块
    this.playAudioChunk(audioData)
    
    // 更新播放进度
    this.updateProgress()
    
    // 计算下一个块的播放时间
    const duration = audioData.byteLength / 2 / this.sampleRate * 1000 // 转换为毫秒
    this.bufferPlaybackTimer = window.setTimeout(() => {
      this.playNextBufferChunk()
    }, duration)
  }


  /**
   * 处理流结束事件
   */
  private handleStreamEnd(): void {
    // 等待更长时间确保所有音频数据播放完成
    setTimeout(() => {
      if (this.isPlaying) {
        this.stop()
      }
    }, 2000) // 增加到2秒，确保音频播放完成
  }

  /**
   * 检查是否是填充数据（全为 'A' 的重复数据）
   */
  private isPaddingData(base64Data: string): boolean {
    // 检查是否主要由重复的 'A' 字符组成
    const aCount = (base64Data.match(/A/g) || []).length
    const totalLength = base64Data.length
    
    // 如果数据太短，不认为是填充数据
    if (totalLength < 10) return false
    
    // 如果 80% 以上是 'A'，认为是填充数据
    return totalLength > 0 && aCount / totalLength > 0.8
  }

  /**
   * 检查音频数据是否有效（不是全零数据）
   */
  private isValidAudioData(audioData: ArrayBuffer): boolean {
    const view = new Int16Array(audioData)
    let nonZeroCount = 0
    
    // 检查是否有足够的非零样本
    for (let i = 0; i < view.length; i++) {
      if (view[i] !== 0) {
        nonZeroCount++
      }
    }
    
    // 如果非零样本少于 5%，认为是无效数据
    return nonZeroCount / view.length > 0.05
  }

  /**
   * 清理 base64 数据
   */
  private cleanBase64Data(base64Data: string): string {
    // 移除空白字符
    base64Data = base64Data.replace(/\s+/g, '')
    
    // 移除非法字符
    if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data)) {
      base64Data = base64Data.replace(/[^A-Za-z0-9+/=]/g, '')
    }
    
    // 补齐填充字符
    while (base64Data.length % 4 !== 0) {
      base64Data += '='
    }
    
    return base64Data
  }

  /**
   * 解码 base64 为 ArrayBuffer
   */
  private decodeBase64ToArrayBuffer(base64Data: string): ArrayBuffer {
    const binaryString = atob(base64Data)
    const bytes = new Uint8Array(binaryString.length)
    
    for (let i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i)
    }
    
    return bytes.buffer
  }

  /**
   * 播放音频数据块
   */
  private playAudioChunk(pcmData: ArrayBuffer): void {
    if (!this.audioContext) return

    try {
      const ctx = this.audioContext
      const channels = 1
      const sampleCount = pcmData.byteLength / 2

      // 创建 AudioBuffer
      const buffer = ctx.createBuffer(channels, sampleCount, this.sampleRate)
      const channelData = buffer.getChannelData(0)

      // 转换 PCM16 到 Float32
      const view = new Int16Array(pcmData)
      for (let i = 0; i < view.length; i++) {
        channelData[i] = view[i] / 32768.0
      }

      // 创建音频源
      const source = ctx.createBufferSource()
      source.buffer = buffer
      source.connect(ctx.destination)

      // 计算播放时间
      const currentTime = ctx.currentTime
      const startTime = Math.max(currentTime, this.nextStartTime)
      source.start(startTime)

      // 更新下次播放时间
      this.nextStartTime = startTime + buffer.duration


    } catch (error) {
      this.handleError(`播放音频块失败: ${error}`)
    }
  }

  /**
   * 更新播放进度
   */
  private updateProgress(): void {
    if (this.callbacks.onProgress) {
      const currentTime = Date.now()
      const elapsed = currentTime - this.startTime
      this.callbacks.onProgress({
        duration: elapsed / 1000, // 转换为秒
        currentTime: elapsed / 1000
      })
    }
  }

  /**
   * 设置超时
   */
  private setupTimeout(): void {
    this.timeoutId = window.setTimeout(() => {
      if (this.eventSource && this.eventSource.readyState !== EventSource.CLOSED) {
        // 如果已经收到数据，给更多时间等待流结束
        if (this.hasReceivedData) {
          const timeSinceLastData = Date.now() - this.lastDataTime
          if (timeSinceLastData < 8000) { // 如果最近8秒内有数据，再等待8秒
            
            this.setupTimeout()
            return
          }
        }
        
        this.handleError('播放超时')
        this.stop()
      }
    }, this.timeout)
  }

  /**
   * 处理错误
   */
  private handleError(message: string): void {
    this.callbacks.onError?.(message)
    this.stop()
  }
}

/**
 * 便捷的语音 SSE 播放函数
 * @param url SSE 流的 URL
 * @param options 播放选项
 */
export async function playVoiceSSE(url: string, options?: VoiceSSEPlayerOptions): Promise<VoiceSSEPlayer> {
  const player = new VoiceSSEPlayer(options)
  await player.play(url)
  return player
}
